package com.xyy.saas.payment.adpater.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xyy.saas.payment.adpater.PaymentAdapter;
import com.xyy.saas.payment.adpater.abchinaLoan.core.req.OrderChangeDto;
import com.xyy.saas.payment.adpater.abchinaLoan.core.req.OrderCheckDto;
import com.xyy.saas.payment.adpater.xydLoan.core.dto.*;
import com.xyy.saas.payment.adpater.xydLoan.core.enums.XydLoanResultStatus;
import com.xyy.saas.payment.adpater.xydLoan.core.req.*;
import com.xyy.saas.payment.adpater.xydLoan.core.resp.XydAuthCreditResp;
import com.xyy.saas.payment.adpater.xydLoan.service.XydLoanService;
import com.xyy.saas.payment.common.Constants;
import com.xyy.saas.payment.common.enums.AmountTypeEnum;
import com.xyy.saas.payment.common.exception.PaymentException;
import com.xyy.saas.payment.cores.bo.*;
import com.xyy.saas.payment.cores.enums.*;
import com.xyy.saas.payment.cores.param.TransactionParam;
import com.xyy.saas.payment.cores.param.XYDPushLoanSceneDto;
import com.xyy.saas.payment.cores.vo.FinanceAccountInfoVo;
import com.xyy.saas.payment.dao.model.PrePayOrder;
import com.xyy.saas.payment.dao.model.RefundOrder;
import com.xyy.saas.payment.dao.model.SeparateOrder;
import com.xyy.saas.payment.merchant.core.dto.AccountTransferDto;
import com.xyy.saas.payment.merchant.core.dto.AccountTransferRefundDto;
import com.xyy.saas.payment.merchant.core.dto.FrozenDto;
import com.xyy.saas.payment.payment.core.bo.RefundBO;
import com.xyy.saas.payment.payment.core.dto.OrderClosedDto;
import com.xyy.saas.payment.payment.core.dto.PayDto;
import com.xyy.saas.payment.payment.core.dto.PrepayDto;
import com.xyy.saas.payment.payment.core.dto.RefundDto;
import com.xyy.saas.payment.payment.core.service.PrePayOrderService;
import com.xyy.saas.payment.service.PayAccountService;
import com.xyy.saas.payment.service.SeparateOrderService;
import com.xyy.saas.payment.util.AmountCastUtil;
import com.xyy.saas.payment.util.ApolloUtil;
import com.xyy.saas.payment.util.JSONUtils;
import com.xyy.saas.payment.util.RedisUtil;
import com.xyy.saas.payment.util.retry.RetryStrategy;
import com.xyy.saas.payment.util.retry.RetryTradeType;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * @author wangzeqi
 */
@Slf4j
@Component
@DependsOn("apolloUtil")
public class XydLoanAdapter implements PaymentAdapter {

    @Resource
    private PayAccountService payAccountService;
    @Resource
    private XydLoanService xydLoanService;
    @Resource
    private PrePayOrderService prePayOrderService;
    /*@Value("${abchinaLoan.app.directUrl:https://wx.abchina.com/webank/main-view/h5ForKED?id=SlTzmeaAHEc%3D}")
    private String abchinaLoanAppDirectUrl;*/
    @Value("${xydLoan.test.switch:false}")
    private Boolean xydLoanTest;
    @Value("${xydLoan.orderClosed.switchV2:true}")
    private Boolean orderClosedSwitchV2;
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private SeparateOrderService separateOrderService;
    @Resource
    private PinganAdapter pinganAdapter;



    @Override
    public PrepayBo prepay(PrepayDto prepayDto) throws PaymentException {
        return prePayOrderService.prepayOrderByPrepayDto(prepayDto);
    }

    @Override
    public RefundBO refund(RefundDto refundDto) throws PaymentException {
        log.info("###小雨点发起退款 XydLoanAdapter#refund refundDto={}", JSONUtils.toJSON(refundDto));

        // 参数校验
        checkRefundParam(refundDto);

        // 判断正向单是否已结算完成
        checkForwardPayOrderSettled(refundDto);

        // 退款lock
        String xydCreditRefundLockKey = RedisUtil.PREFIX_XYD_CREDIT_REFUND_LOCK + refundDto.getPayNo();

        if (!redisUtil.tryLock(xydCreditRefundLockKey)) {
            log.info("###小雨点发起退款 get lock fail, {}-{}", refundDto.getRefundNo(), refundDto.getPayNo());
            // 重试
            throw new PaymentException(ResultCodeEnum.REFUND_BUSY);
        }

        try {
            // 查询订单未还本金
            BigDecimal unpaidPrincipal = getTradeOrderUnpaidPrincipal(refundDto.getPayNo(), refundDto.getPayer());
            // 退款金额
            BigDecimal refundAmount = AmountCastUtil.castFen2YuanByBigDecimal(refundDto.getAmount());
            log.info("###小雨点发起退款 {} unpaidPrincipal:{}, refundAmount:{}", refundDto.getRefundNo(), unpaidPrincipal, refundAmount);

            if (refundAmount.compareTo(unpaidPrincipal) <= 0) {
                // 走小雨点退款：平台退款专户——>客户在小雨点授信额度
                return refundByXydCreditLoan(refundDto);
            } else {
                // 退款转入平安商户  卖家——>买家平安账户
                return refundByTransPinganAccount(refundDto);
            }

        } finally {
            redisUtil.unLock(xydCreditRefundLockKey);
        }
    }

    private void checkForwardPayOrderSettled(RefundDto refundDto) throws PaymentException {
        boolean selfSupportBusinessOrderType = pinganAdapter.isSelfSupportBizOrderType(refundDto.getReceiverId(), refundDto.getBusinessOrderType());
        boolean popBusinessOrderType = pinganAdapter.isPopBizOrderType(refundDto.getBusinessOrderType());

        boolean alreadySettled = false;
        if (selfSupportBusinessOrderType) {
            // 判断自营结算完成
            SeparateOrder separateOrders = separateOrderService.queryOneByBusinessOrderNo(refundDto.getPayNo() + "_" + AmountTypeEnum.XYD_CREDIT_PAYER_PAY_ACCT_TRANS.name());
            if (separateOrders != null && Objects.equals(separateOrders.getStatus(), SeparateOrder.STATUS_FINISH)) {
                alreadySettled = true;
            }
        } else if (popBusinessOrderType) {
            // 判断POP结算完成
            SeparateOrder separateOrder = separateOrderService.queryOneByBusinessOrderNo(refundDto.getPayNo() + "_" + AmountTypeEnum.XYD_CREDIT_FREEZE.name());
            if (separateOrder != null && Objects.equals(separateOrder.getStatus(), SeparateOrder.STATUS_FINISH)) {
                alreadySettled = true;
            }
        }

        if (!alreadySettled) {
            log.error("###小雨点发起退款 正向单未结算, {}-{}", refundDto.getRefundNo(), refundDto.getPayNo());
            throw new PaymentException(ResultCodeEnum.ORDER_NONE_SETTLE);
        }

    }

    private RefundBO refundByTransPinganAccount(RefundDto refundDto) throws PaymentException {
        log.info("###小雨点发起退款 refundByTransPinganAccount {}", JSONUtils.toJSON(refundDto));

        // 卖家提现lock
        String popReceiverWithdrawLockKey = null;
        // 获取支付账户id
        String payerAccountId = getPayerAccountId(refundDto);

        try {
            boolean selfSupportBusinessOrderType = pinganAdapter.isSelfSupportBizOrderType(refundDto.getReceiverId(), refundDto.getBusinessOrderType());
            boolean popBusinessOrderType = pinganAdapter.isPopBizOrderType(refundDto.getBusinessOrderType());

            if (selfSupportBusinessOrderType) {
                // 自营退款
                processSelfSupportTransXydCreditRefund(refundDto, payerAccountId);
            } else if (popBusinessOrderType) {
                // POP退款
                popReceiverWithdrawLockKey = RedisUtil.D_AMOUNT_LOCK + refundDto.getReceiverId();
                // 提现锁，可重入无需判断加锁返回值
                redisUtil.setNx(popReceiverWithdrawLockKey);
                processPopTransXydCreditRefund(refundDto);
            } else {
                log.error("###小雨点发起退款 refundByTransPinganAccount unknown businessOrderType, {}-{}", refundDto.getRefundNo(), refundDto.getBusinessOrderType());
                throw new PaymentException(ResultCodeEnum.UN_SUPPORT_METHOD, "退款未知业务订单类型");
            }

            RefundBO refundBO = new RefundBO();
            refundBO.setRefundNo(refundDto.getRefundNo());
            refundBO.setStatus(RefundOrder.STATUS_FINISH);
            refundBO.setDate(new Date());
            refundBO.setChannelTransactionNo("");
            refundBO.setChannelRefundNo("");
            refundBO.setRemark(Constants.REFUND_ROUTE_TIPS_XYD_LOAN);
            return refundBO;
        } finally {
            if (popReceiverWithdrawLockKey != null) {
                redisUtil.unLock(popReceiverWithdrawLockKey);
            }
        }
    }

    private void processPopTransXydCreditRefund(RefundDto refundDto) throws PaymentException {
        // 退款解冻、支付单分润解冻 lock
        String xydCreditPayProfitSharingLock = RedisUtil.PREFIX_XYD_CREDIT_PAY_PROFITSHARING_LOCK + refundDto.getPayNo();
        if (!redisUtil.tryLock(xydCreditPayProfitSharingLock)) {
            log.error("###小雨点发起退款 get payProfitSharingLock fail, {}-{}", refundDto.getRefundNo(), refundDto.getPayNo());
            throw new PaymentException(ResultCodeEnum.PROFITSHARING_BUSY);
        }

        try {
            // 判断正向单是否分润
            List<SeparateOrder> puSeparateOrders = separateOrderService.querySeparateOrderByBusinessOrderNo(refundDto.getPayNo() + "_" + AmountTypeEnum.XYD_CREDIT_PU.name());
            boolean payOrderShared = CollectionUtil.isNotEmpty(puSeparateOrders) && SeparateOrder.STATUS_FINISH.equals(puSeparateOrders.get(0).getStatus());

            if (payOrderShared) {
                log.info("###小雨点发起退款 正向单已分润-垫资, {}-{}", refundDto.getRefundNo(), refundDto.getPayNo());
                // 垫资：平台退款专用户——>卖家可提现
                AccountTransferDto accountTransferDto = AccountTransferDto.builder().
                        transferType(AccountTransferDto.TRANSFER_PAY).
                        businessIdType(refundDto.getBusinessOrderType()).
                        outAccountId(ApolloUtil.getProperty(Constants.EC_PINGAN_REFUND_ACCOUNT_ID)).
                        inAccountId(refundDto.getReceiverId()).
                        amount(refundDto.getAmount()).
                        businessOrderNo(refundDto.getRefundNo()).
                        remark(refundDto.getBusinessRefundNo() + "|" + "退款时-退款垫资").build();
                RetryStrategy.RETRY_2_ATTEMPTS.executeWithRetry(() -> pinganAdapter.asyncAccountTransferAndPoll(accountTransferDto), RetryTradeType.XYD_CREDIT_REFUND, true);
            } else {
                log.info("###小雨点发起退款 正向单未分润-退款解冻 {}-{}", refundDto.getRefundNo(), refundDto.getPayNo());
                // 退款解冻：卖家冻结——>卖家可提现
                AccountTransferDto accountTransferDto = AccountTransferDto.builder().
                        transferType(FrozenDto.FUCC_FLAG_6).
                        businessIdType(refundDto.getBusinessOrderType()).
                        outAccountId(refundDto.getReceiverId()).
                        inAccountId(refundDto.getReceiverId()).
                        amount(refundDto.getAmount()).
                        orderNo(refundDto.getPayNo() + "_" + AmountTypeEnum.XYD_CREDIT_FREEZE.name()).
                        businessOrderNo(refundDto.getRefundNo() + "_" + AmountTypeEnum.XYD_CREDIT_UNFREEZE.name()).
                        bizNo(refundDto.getRefundNo()).
                        extra("退款解冻").build();
                RetryStrategy.RETRY_2_ATTEMPTS.executeWithRetry(() -> pinganAdapter.asyncUnfreezeMembershipAndPoll(accountTransferDto), RetryTradeType.XYD_CREDIT_REFUND, true);
            }

            // 会员间交易退款：卖家可提现——>买家可提现
            AccountTransferRefundDto accountTransferRefundDto = AccountTransferRefundDto.builder().
                    businessIdType(refundDto.getBusinessOrderType()).
                    amount(Math.abs(refundDto.getAmount())).
                    businessOrderNo(refundDto.getPayNo() + "_" + AmountTypeEnum.XYD_CREDIT_PAYER_PAY_ACCT_TRANS.name()).
                    businessRefundNo(refundDto.getRefundNo() + "_" + AmountTypeEnum.XYD_CREDIT_PAY_ACCT_TRANS_REFUND.name())
                    .build();
            RetryStrategy.RETRY_2_ATTEMPTS.executeWithRetry(() -> pinganAdapter.asyncTransferRefundAndPoll(accountTransferRefundDto), RetryTradeType.XYD_CREDIT_REFUND, true);

        } finally {
            redisUtil.unLock(xydCreditPayProfitSharingLock);
        }
    }

    private void processSelfSupportTransXydCreditRefund(RefundDto refundDto, String payerAccountId) {
        // 会员间交易退款
        AccountTransferRefundDto accountTransferRefundDto = AccountTransferRefundDto.builder().
                businessIdType(refundDto.getBusinessOrderType()).
                amount(Math.abs(refundDto.getAmount())).
                businessOrderNo(refundDto.getPayNo() + "_" + AmountTypeEnum.XYD_CREDIT_PAYER_PAY_ACCT_TRANS.name()).
                businessRefundNo(refundDto.getRefundNo() + "_" + AmountTypeEnum.XYD_CREDIT_PAY_ACCT_TRANS_REFUND.name())
                .build();
        RetryStrategy.RETRY_2_ATTEMPTS.executeWithRetry(() -> pinganAdapter.asyncTransferRefundAndPoll(accountTransferRefundDto), RetryTradeType.PINGAN_CREDIT_REFUND, true);
    }

    private String getPayerAccountId(RefundDto refundDto) throws PaymentException {
        String payerAccountId = payAccountService.getAccountIdByBusiness(BusinessIdTypeEnum.EC.getType(), refundDto.getPayer());
        if (StringUtils.isBlank(payerAccountId)) {
            throw new PaymentException(ResultCodeEnum.USER_NOT_FOUND);
        }
        return payerAccountId;
    }

    private RefundBO refundByXydCreditLoan(RefundDto refundDto) throws PaymentException {
        log.info("###小雨点发起退款 refundByXydCreditLoan {}", refundDto.getRefundNo());

        // 调小雨点【退货】
        try {
            XydLoanSalesReturnReq xydLoanSalesReturnReq = new XydLoanSalesReturnReq()
                    .setReturnAmount(AmountCastUtil.castFen2Yuan(refundDto.getAmount()))
                    .setThirdUserNo(refundDto.getPayer())
                    .setSequenceNumber(refundDto.getRefundNo())
                    .setXydLoanNo(refundDto.getOrigChannelTransactionNo())
                    ;
            xydLoanService.loanSalesReturn(xydLoanSalesReturnReq);
        } catch (Exception e) {
            log.error("###小雨点发起退款 [退货] 异常, {}-{}", refundDto.getRefundNo(), e.getMessage(), e);
            throw e;
        }

        RefundBO refundBO = new RefundBO();
        refundBO.setRefundNo(refundDto.getRefundNo());
        refundBO.setStatus(RefundOrder.STATUS_IN);
        refundBO.setDate(new Date());
        refundBO.setChannelTransactionNo("");
        refundBO.setChannelRefundNo("");
        return refundBO;
    }

    private BigDecimal getTradeOrderUnpaidPrincipal(String payNo, String merchantId) throws PaymentException {
        try {
            XydSearchLoanResultReq xydSearchLoanResultReq = new XydSearchLoanResultReq()
                    .setThirdOrderNo(payNo)
                    .setThirdUserNo(merchantId);
            XydLoanResultDto xydLoanResultDto = xydLoanService.searchLoanResult(xydSearchLoanResultReq);
            if (xydLoanResultDto == null || StringUtils.isBlank(xydLoanResultDto.getLoanStatus()) ||
                    StringUtils.isBlank(xydLoanResultDto.getUnpaidPrincipal())) {
                throw new PaymentException(ResultCodeEnum.PARAM_ERROR, "[查询放款结果]小雨点返回参数缺失");
            }

            XydLoanResultStatus xydLoanResultStatus = XydLoanResultStatus.fromStatus(xydLoanResultDto.getLoanStatus());
            if (xydLoanResultStatus == null) {
                throw new PaymentException(ResultCodeEnum.PARAM_ERROR, "[查询放款结果]小雨点返回状态值错误");
            }
            if (xydLoanResultStatus != XydLoanResultStatus.SUCCESSED) {
                throw new PaymentException(ResultCodeEnum.PARAM_ERROR, "[查询放款结果]小雨点返回放款状态不是放款成功");
            }
            BigDecimal unpaidPrincipal = new BigDecimal(xydLoanResultDto.getUnpaidPrincipal());
            if (unpaidPrincipal.scale() > 2) {
                throw new PaymentException(ResultCodeEnum.PARAM_ERROR, "[查询放款结果]小雨点金额精确位错误");
            }
            return unpaidPrincipal;
        } catch (Exception e) {
            log.error("###小雨点发起退款查询订单未还本金异常, {}-{}, errorMsg:{}", payNo, merchantId, e.getMessage(), e);
            throw e;
        }
    }

    private void checkRefundParam(RefundDto refundDto) throws PaymentException {
        if (StringUtils.isBlank(refundDto.getPayNo()) ||
                refundDto.getAmount() == null ||
                StringUtils.isBlank(refundDto.getReceiverId()) ||
                StringUtils.isBlank(refundDto.getPayer()) ||
                StringUtils.isBlank(refundDto.getRefundNo()) ||
                StringUtils.isBlank(refundDto.getOrigChannelTransactionNo())) {
            log.error("###小雨点发起退款 XydLoanAdapter#refund param error, refundDto:{}", JSONUtils.toJSON(refundDto));
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR);
        }
    }

    @SneakyThrows
    private String convertAbchinaLoanRefundType(Integer refundType) {
        if (refundType == null) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR, "refundType is null");
        }
        if (refundType != 1 && refundType != 2) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR, "refundType error");
        }
        // 农行入参 0-部分退  1-全退
        return refundType == 1 ? "0" : "1";
    }

    @Override
    public PayBo pay(PayDto payDto) throws PaymentException {
        log.info("pay {}", JSONObject.toJSONString(payDto));

        // 订单核验
        XydAuthCreditDto xydAuthCreditDto = new XydAuthCreditDto();
        String mobileTerminalType = payDto.getMobileTerminalType();
        if (PayModeEnum.PC.getMode().equals(payDto.getPayMode())) {
            xydAuthCreditDto.setPlatType("PC");
        } else if (PayModeEnum.APP.getMode().equals(payDto.getPayMode())) {
            xydAuthCreditDto.setPlatType("H5");
        }

        xydAuthCreditDto.setDeviceNo(payDto.getPayerId());
        xydAuthCreditDto.setThirdUserNo(payDto.getPayerId());
        xydAuthCreditDto.setThirdOrderNo(payDto.getPayNo());

        XydPayAuthLoanReq req = new XydPayAuthLoanReq();
        req.setAuth(xydAuthCreditDto);
        XydAuthCreditResp xydAuthCreditResp = xydLoanService.authLoan(req);
        // 核验结果解析
//        handleOrderCheckResult(payDto.getPayNo(), xydAuthCreditResp);

        PayBo payBo = new PayBo();
        payBo.setPayNO(payDto.getPayNo());
        payBo.setPayLoad(xydAuthCreditResp.getH5Url());
        return payBo;
    }

//    private void handleOrderCheckResult(String payNo, XydAuthCreditResp xydAuthCreditResp) throws PaymentException {
//        AbchinaLoanOrderCheckResEnum checkResEnum = AbchinaLoanOrderCheckResEnum.fromCode(orderCheckResp.getCheckRes());
//        if (checkResEnum == null) {
//            throw new PaymentException(ResultCodeEnum.ABCHINA_ORDERCHECK_RESULT_UNKNOWN);
//        }
//        switch (checkResEnum) {
//            case CONTRACT_STATUS_INVALID:
//                throw new PaymentException(ResultCodeEnum.ABCHINA_ORDERCHECK_CONTRACT_STATUS_INVALID);
//            case INSUFFICIENT_BALANCE:
//                throw new PaymentException(ResultCodeEnum.ABCHINA_ORDERCHECK_INSUFFICIENT_BALANCE);
//            case INVALID_PAYMENT_ACCOUNT:
//                throw new PaymentException(ResultCodeEnum.ABCHINA_ORDERCHECK_INVALID_PAYMENT_ACCOUNT);
//            case UNPAID_ORDER_EXISTS:
//                log.error("农行订单支付资质核验存在未支付订单, payNo:{}, 未支付订单:{}", payNo, orderCheckResp.getTrCode());
//                throw new PaymentException(ResultCodeEnum.ABCHINA_ORDERCHECK_UNPAID_ORDER_EXISTS);
//            case VERIFIED_PASS:
//                break; // 校验通过，继续执行
//            default:
//                throw new PaymentException(ResultCodeEnum.ABCHINA_ORDERCHECK_RESULT_UNPASS, checkResEnum.getDescription());
//        }
//    }



    private void fillPayOrderCheckDto(OrderCheckDto orderCheckDto, PayDto payDto) {
        orderCheckDto.setOrderId(payDto.getPayNo())
                .setUscc(payDto.getMerchantUscc())
                .setOrderAmt(AmountCastUtil.castFen2YuanByBigDecimal(payDto.getAmount()))
                .setEntrustedAcc(payDto.getReceiveAccount());
    }

    @Override
    public OrderClosedBo orderClosed(OrderClosedDto orderClosedDto) throws PaymentException {
        log.info("###小雨点订单取消 orderClosed orderClosedDto={}", JSONUtils.toJSON(orderClosedDto));

        if (StringUtils.isBlank(orderClosedDto.getPayNo()) || StringUtils.isBlank(orderClosedDto.getPayer())) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR);
        }

        OrderClosedBo orderClosedBo;
        if (Boolean.TRUE.equals(orderClosedSwitchV2)) {
            orderClosedBo = xydLoanOrderClosedV2(orderClosedDto);
        } else {
            orderClosedBo = xydLoanOrderClosed(orderClosedDto);
        }

        return orderClosedBo;
    }

    private OrderClosedBo xydLoanOrderClosedV2(OrderClosedDto orderClosedDto) throws PaymentException {

        // 查询放款结果
        try {
            XydSearchLoanResultReq xydSearchLoanResultReq = new XydSearchLoanResultReq()
                    .setThirdOrderNo(orderClosedDto.getPayNo())
                    .setThirdUserNo(orderClosedDto.getPayer());
            XydLoanResultDto xydLoanResultDto = xydLoanService.searchLoanResult(xydSearchLoanResultReq);
            if (xydLoanResultDto == null || StringUtils.isBlank(xydLoanResultDto.getLoanStatus())) {
                throw new PaymentException(ResultCodeEnum.PARAM_ERROR, "[查询放款结果]小雨点返回参数缺失");
            }
            log.info("###小雨点订单取消 查询放款结果, {}-{}", orderClosedDto.getPayNo(), xydLoanResultDto.getLoanStatus());
            XydLoanResultStatus xydLoanResultStatus = XydLoanResultStatus.fromStatus(xydLoanResultDto.getLoanStatus());
            if (xydLoanResultStatus == null) {
                throw new PaymentException(ResultCodeEnum.PARAM_ERROR, "[查询放款结果]小雨点返回状态值错误");
            }

            if (xydLoanResultStatus == XydLoanResultStatus.NONE || xydLoanResultStatus == XydLoanResultStatus.LOAN_CANCEL ||
                    xydLoanResultStatus == XydLoanResultStatus.FAILED) {
                return new OrderClosedBo()
                        .setPayNo(orderClosedDto.getPayNo())
                        .setStatus(OrderClosedBo.SUCCESS);
            }

            if (xydLoanResultStatus == XydLoanResultStatus.PROCCESSING || xydLoanResultStatus == XydLoanResultStatus.SUCCESSED) {
                // 放款中、放款成功不允许取消
                log.error("###小雨点订单取消失败, 放款中或放款成功不允许取消, {}-{}", orderClosedDto.getPayNo(), xydLoanResultStatus);
                throw new PaymentException(ResultCodeEnum.ORDER_CLOSED_ERROR, "放款中或放款成功不允许取消");
            }

        } catch (Exception e) {
            log.error("###小雨点订单取消 查询放款结果异常, {}-{}", orderClosedDto.getPayNo(), e.getMessage(), e);

            /*if (e instanceof PaymentException) {
                PaymentException pe = (PaymentException) e;
                String errMsg = pe.getErrMsg();

                // 提款h5未提交，小雨点不会生成订单
                if (StringUtils.isNotBlank(errMsg) && errMsg.contains("找不到相关数据")) {
                    return new OrderClosedBo()
                            .setPayNo(orderClosedDto.getPayNo())
                            .setStatus(OrderClosedBo.SUCCESS);
                }
                throw e;
            }*/

            throw e;
        }


        // 取消放款
        OrderClosedBo orderClosedBo = xydLoanOrderClosed(orderClosedDto);
        return orderClosedBo;
    }

    private OrderClosedBo xydLoanOrderClosed(OrderClosedDto orderClosedDto) throws PaymentException {
        XydLoanCancelReq xydLoanCancelReq = new XydLoanCancelReq();
        xydLoanCancelReq.setThirdOrderNo(orderClosedDto.getPayNo());
        xydLoanCancelReq.setThirdUserNo(orderClosedDto.getPayer());

        try {
            xydLoanService.loanCancel(xydLoanCancelReq);
            log.info("###小雨点订单取消 success, {}-{}", orderClosedDto.getPayNo(), orderClosedDto.getPayer());
        } catch (Exception e) {
            log.error("###小雨点订单取消 [取消放款] 异常, {}-{}", orderClosedDto.getPayNo(), e.getMessage(), e);
            throw e;
        }

        OrderClosedBo orderClosedBo = new OrderClosedBo()
                .setPayNo(orderClosedDto.getPayNo())
                .setStatus(OrderClosedBo.SUCCESS);
        return orderClosedBo;
    }

    private void fillOrderClosedDto(OrderChangeDto orderChangeDto, OrderClosedDto orderClosedDto) {
        orderChangeDto.setOrderId(orderClosedDto.getPayNo())
                .setChgType("0");
    }

    @Override
    public QueryResultBo queryPayStatus(String tradeNo, PrePayOrder prePayOrder) throws PaymentException {
        // todo
        return null;
    }

    @Override
    public TransactionResultBO queryTransaction(TransactionParam param) throws PaymentException {
        log.info("###小雨点查询交易结果 queryTransaction param:{}", JSON.toJSONString(param));
        if (StringUtils.isBlank(param.getTradeNo()) || StringUtils.isBlank(param.getPayer())) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR);
        }

        /*if (Boolean.TRUE.equals(xydLoanTest)) {
            return mockQueryTransactionSuccess(param);
        }*/

        XydSearchLoanResultReq xydSearchLoanResultReq = new XydSearchLoanResultReq()
                .setThirdUserNo(param.getPayer())
                .setThirdOrderNo(param.getTradeNo());

        XydLoanResultDto xydLoanResultDto;
        try {
            xydLoanResultDto = xydLoanService.searchLoanResult(xydSearchLoanResultReq);
        } catch (Exception e) {
            log.error("###小雨点查询交易结果 [查询放款结果] 异常, {}-{}", param.getTradeNo(), e.getMessage(), e);
            throw e;
        }

        if (xydLoanResultDto == null || StringUtils.isBlank(xydLoanResultDto.getLoanStatus())) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR, "[查询放款结果]小雨点返回参数缺失");
        }

        XydLoanResultStatus xydLoanResultStatus = XydLoanResultStatus.fromStatus(xydLoanResultDto.getLoanStatus());
        if (xydLoanResultStatus == null) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR, "[查询放款结果]小雨点返回状态值错误");
        }

        TransactionStatusEnum transactionStatusEnum = getTransactionStatus(xydLoanResultStatus);
        if (transactionStatusEnum == null) {
            log.error("###小雨点查询交易结果 [查询放款结果]小雨点返回状态值未知, {}-{}", param.getTradeNo(), xydLoanResultStatus);
        }

        TransactionResultBO resultBO = new TransactionResultBO();
        resultBO.setTradeNo(param.getTradeNo());
        resultBO.setStatus(transactionStatusEnum != null ? transactionStatusEnum.getCode() : null);
        resultBO.setSuccessTime(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
        resultBO.setChannelChargeNo(param.getTradeNo());
        resultBO.setChannel(param.getChannel());
        return resultBO;
    }

    private TransactionStatusEnum getTransactionStatus(XydLoanResultStatus xydLoanResultStatus) {
        switch (xydLoanResultStatus) {
            case NONE:
                return TransactionStatusEnum.NOT_FOUNT;
            case ESIGN_WAITING:
            case ESIGN_SUBMIT:
            case PROCCESSING:
                return TransactionStatusEnum.TRANSACTING;
            case LOAN_CANCEL:
            case DECLINE:
            case FAILED:
                return TransactionStatusEnum.FAIL;
            case SUCCESSED:
                return TransactionStatusEnum.SUCCESS;
            default:
                return null;
        }
    }

    /**
     * 贷款申请状态查询
     */
//    public FinanceAccountInfoVo queryLoanStatus(String uscc) {
//        QueryLoanDto loanDto = new QueryLoanDto();
//        loanDto.setUscc(uscc);
//        loanDto.checkParam();
////        String loanStatus = xydLoanService.queryLoanStatus(loanDto).getLoanStatus();
//        return FinanceAccountInfoVo.builder().loanStatus(loanStatus).build();
//    }

    private TransactionResultBO getTransactionResultBO(TransactionParam param, AbchinaLoanOrderStatusEnum abchinaLoanOrderStatusEnum, Long successTime) {
        TransactionStatusEnum transactionStatusEnum = TransactionStatusEnum.convertAbchinaLoan(abchinaLoanOrderStatusEnum);
        TransactionResultBO queryResultBo = new TransactionResultBO();
        queryResultBo.setTradeNo(param.getTradeNo());
        queryResultBo.setStatus(transactionStatusEnum != null ? transactionStatusEnum.getCode() : null);
        queryResultBo.setSuccessTime(successTime);
        queryResultBo.setChannelChargeNo(param.getTradeNo());
        queryResultBo.setChannel(param.getChannel());
        return queryResultBo;
    }

    private TransactionResultBO mockQueryTransactionSuccess(TransactionParam param) {
        TransactionResultBO queryResultBo = new TransactionResultBO();
        queryResultBo.setTradeNo(param.getTradeNo());
        queryResultBo.setStatus(TransactionStatusEnum.SUCCESS.getCode());
        queryResultBo.setSuccessTime(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
        queryResultBo.setChannelChargeNo(param.getTradeNo());
        queryResultBo.setChannel(param.getChannel());
        //queryResultBo.setAmount(resultBo.getAmount());
        return queryResultBo;
    }

    public FinanceAccountInfoVo searchCreditResult(String businessUserId, String uscc) throws PaymentException {
        XydCreditResultDto creditDto = xydLoanService.searchCreditResult(businessUserId, uscc);
        if (creditDto == null) {
            return null;
        }
        FinanceAccountInfoVo infoVo = FinanceAccountInfoVo.builder()
                .approveAmount(creditDto.getCreditAmount())
                .thirdApplyNo(creditDto.getApplicationNo())
                .availableAmount(creditDto.getAvailableCreditAmount())
                .build();
        if (StringUtils.isNotEmpty(creditDto.getFailureTime())) {
            infoVo.setAmountEndDate(creditDto.getFailureTime().substring(0, 10).replace("-",""));
        }
        return infoVo;
    }

    /**
     * 贷中数据推送
     * @param xydPushLoanSceneDto
     * @return
     */
    public boolean pushLoanSceneData(XYDPushLoanSceneDto xydPushLoanSceneDto) {
        //组装小雨点贷中推送数据
        XydLoanDataSyncReq xydLoanDataSyncReq = new XydLoanDataSyncReq();
        xydLoanDataSyncReq.setThirdUserNo(String.valueOf(xydPushLoanSceneDto.getMerchantId()));

        //组装订单数据
        List<XYDPushLoanSceneDto.XydOrderInfo> orderList = xydPushLoanSceneDto.getOrderList();
        if (CollectionUtil.isNotEmpty(orderList) && orderList.get(0) != null) {
            XYDPushLoanSceneDto.XydOrderInfo xydOrderInfo = orderList.get(0);
            XydLoaningOrderInfoDto loaningOrderInfoDto = new XydLoaningOrderInfoDto();
            loaningOrderInfoDto.setSummaryMonth(xydOrderInfo.getOrderCreateMth());
            loaningOrderInfoDto.setOrderTotalCount(String.valueOf(xydOrderInfo.getPaidCnt()));
            loaningOrderInfoDto.setOrderAmt(String.valueOf(xydOrderInfo.getPaidAmount()));
            loaningOrderInfoDto.setRefundTotalCount(String.valueOf(xydOrderInfo.getRefundCnt()));
            loaningOrderInfoDto.setRefundOrderAmt(String.valueOf(xydOrderInfo.getRefundAmount()));
            xydLoanDataSyncReq.setOrderInfo(JSON.toJSONString(loaningOrderInfoDto));
        }

        //物流信息集合
        List<XYDPushLoanSceneDto.XydLogisticsData> logisticsList = xydPushLoanSceneDto.getLogisticsList();
        for (XYDPushLoanSceneDto.XydLogisticsData xydLogisticsData : logisticsList) {

            //组装物流数据
            XydLogisticsInfoDto logisticsInfoDto = new XydLogisticsInfoDto();
            logisticsInfoDto.setThirdOrderNo(xydLogisticsData.getTargetOrderId());
            logisticsInfoDto.setLogisticsNo(xydLogisticsData.getWaybillNo());
            logisticsInfoDto.setDistributionType(xydLogisticsData.getDeliveryType());
            logisticsInfoDto.setLogisticsStatus(xydLogisticsData.getCurrentLogisticsState());
            logisticsInfoDto.setReceiveAddress(xydLogisticsData.getReceivingAddress());
            logisticsInfoDto.setReceiveUserName(xydLogisticsData.getMerchantName());
            logisticsInfoDto.setReceiveUserPhoneNo(xydLogisticsData.getMobile());
            logisticsInfoDto.setLogisticsCompanyName(xydLogisticsData.getLogisticsCompanyName());
            logisticsInfoDto.setLogisticsStatusChangeTime(xydLogisticsData.getUpdateTime());
            logisticsInfoDto.setSupplierName(xydLogisticsData.getOrgName());
            xydLoanDataSyncReq.setLogisticsInfo(logisticsInfoDto);
            boolean ret = xydLoanService.loanDataSync(xydLoanDataSyncReq);
            if (!ret) {
                log.error("小雨点 贷中数据推送失败 param：{}", JSON.toJSONString(xydPushLoanSceneDto));
                return false;
            }
        }
        return true;
    }
}
